﻿using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Didaku.Driver.DesktopCamera
{
    public class Camera
    {
        // Constructur  
        public Camera(IntPtr handle, int width, int height)
        {
            mControlPtr = handle;
            mWidth = width;
            mHeight = height;
        }

        // delegate for frame callback  
        public delegate void RecievedFrameEventHandler(byte[] data);
        public event RecievedFrameEventHandler RecievedFrame;

        private IntPtr lwndC; // Holds the unmanaged handle of the control  
        private IntPtr mControlPtr; // Holds the managed pointer of the control  
        private int mWidth;
        private int mHeight;

        private AviCap32.FrameEventHandler mFrameEventHandler; // Delegate instance for the frame callback - must keep alive! gc should NOT collect it  

        // Close the web camera  
        public void CloseWebcam()
        {
            this.capDriverDisconnect(this.lwndC);
        }

        // start the web camera  
        public void StartWebCam()
        {
            byte[] lpszName = new byte[100];
            byte[] lpszVer = new byte[100];

            AviCap32.capGetDriverDescriptionA(0, lpszName, 100, lpszVer, 100);
            this.lwndC = AviCap32.capCreateCaptureWindowA(lpszName, AviCap32.WS_VISIBLE + AviCap32.WS_CHILD, 0, 0, mWidth, mHeight, mControlPtr, 0);

            if (this.capDriverConnect(this.lwndC, 0))
            {
                this.capPreviewRate(this.lwndC, 66);
                this.capPreview(this.lwndC, true);
                AviCap32.BITMAPINFO bitmapinfo = new AviCap32.BITMAPINFO();
                bitmapinfo.bmiHeader.biSize = AviCap32.SizeOf(bitmapinfo.bmiHeader);
                bitmapinfo.bmiHeader.biWidth = 352;
                bitmapinfo.bmiHeader.biHeight = 288;
                bitmapinfo.bmiHeader.biPlanes = 1;
                bitmapinfo.bmiHeader.biBitCount = 24;
                this.capSetVideoFormat(this.lwndC, ref bitmapinfo, AviCap32.SizeOf(bitmapinfo));
                this.mFrameEventHandler = new AviCap32.FrameEventHandler(FrameCallBack);
                this.capSetCallbackOnFrame(this.lwndC, this.mFrameEventHandler);
                AviCap32.SetWindowPos(this.lwndC, 0, 0, 0, mWidth, mHeight, 6);
            }
        }

        // private functions  
        private bool capDriverConnect(IntPtr lwnd, short i)
        {
            return AviCap32.SendMessage(lwnd, AviCap32.WM_CAP_DRIVER_CONNECT, i, 0);
        }

        private bool capDriverDisconnect(IntPtr lwnd)
        {
            return AviCap32.SendMessage(lwnd, AviCap32.WM_CAP_DRIVER_DISCONNECT, 0, 0);
        }

        private bool capPreview(IntPtr lwnd, bool f)
        {
            return AviCap32.SendMessage(lwnd, AviCap32.WM_CAP_SET_PREVIEW, f, 0);
        }

        private bool capPreviewRate(IntPtr lwnd, short wMS)
        {
            return AviCap32.SendMessage(lwnd, AviCap32.WM_CAP_SET_PREVIEWRATE, wMS, 0);
        }

        private bool capSetCallbackOnFrame(IntPtr lwnd, AviCap32.FrameEventHandler lpProc)
        {
            return AviCap32.SendMessage(lwnd, AviCap32.WM_CAP_SET_CALLBACK_FRAME, 0, lpProc);
        }

        private bool capSetVideoFormat(IntPtr hCapWnd, ref AviCap32.BITMAPINFO BmpFormat, int CapFormatSize)
        {
            return AviCap32.SendMessage(hCapWnd, AviCap32.WM_CAP_SET_VIDEOFORMAT, CapFormatSize, ref BmpFormat);
        }

        private void FrameCallBack(IntPtr lwnd, IntPtr lpVHdr)
        {
            AviCap32.VIDEOHDR videoHeader = new AviCap32.VIDEOHDR();
            byte[] VideoData;
            videoHeader = (AviCap32.VIDEOHDR)AviCap32.GetStructure(lpVHdr, videoHeader);
            VideoData = new byte[videoHeader.dwBytesUsed];
            AviCap32.Copy(videoHeader.lpData, VideoData);
            if (this.RecievedFrame != null)
                this.RecievedFrame(VideoData);
        }
    }  
}